classdef mpc_policy  < handle
    
    properties
        past_window_size=60;
        future_window_size=60;
        %         samplingTime = 1;
        control_interval = 15;
        model=[];
    end
    
    methods
        function obj=mpc_policy(model)
            obj.model=model;
        end
        
        function acc_inst=accumulate(obj,acc_inst,t,instance_quantity_to_fire)
            a=instance_quantity_to_fire;
            if (t+60>size(acc_inst,1))
                acc_inst=[acc_inst ; zeros(t+60-size(acc_inst,1),size(instance_quantity_to_fire,2))];
            end
            acc_inst(t+1:t+60,:)=acc_inst(t+1:t+60,:)+ones(60,1)*instance_quantity_to_fire;
        end
        
        function res=predict_choice_outcome(obj, choice,inst,past_util,current_time)
            linear_model = obj.model; 
            start_=1;
            residual=current_time;
            if (current_time>60)
                start_=current_time-60;
                residual=60;
            end
            inst_interval = obj.accumulate(inst( ( start_:size(inst,1)) ,:),...
                current_time,... %60,... %  current_time,
                choice);
            
            %    past_util = system.past_util();
            util_interval =  past_util((start_:end),:);
            util_interval = [util_interval  ;  zeros(60,1)];
            
            %  yp=obj.runModel( m, inst_interval);
            yp=linear_model.predictWithModel( inst_interval , util_interval);
            res_util = yp(size(yp,1)-60:end);
            res_util_est = yp(size(yp,1)-60-residual+1:size(yp,1)-60);
            
            res=struct('inst',inst_interval,'util',res_util, 'util_est',res_util_est);
        end
        
        function choices=generate_choices(obj)
            % a-star
            % ceil(240 ./ (prices_amz().get_compute_units))
            possibilities = ceil(240 ./ (prices_amz().get_compute_units));
            % 240   240    60    30    37    19    10    48    12     8      8
            A = {(0:1:40) (0:1:20) (0:2:60) (0:2:30) (0:2:37) (0:2:19) (0:1:10) (0:3:48) (0:2:12) (0:1:8) (0:1:8)};
            choices=    [ A{1}' zeros(length(A{1}),11-1)];
            
            %             choices=vertcat(... %            [ A{1}' zeros(length(A{1}),11-1)],...
            %             [ zeros(length(A{2}),1) A{2}' zeros(length(A{2}),11-2)],...
            %             [ zeros(length(A{3}),2) A{3}' zeros(length(A{3}),11-3)],...
            %             [ zeros(length(A{4}),3) A{4}' zeros(length(A{4}),11-4)],...
            %             [ zeros(length(A{5}),4) A{5}' zeros(length(A{5}),11-5)],...
            %             [ zeros(length(A{6}),5) A{6}' zeros(length(A{6}),11-6)],...
            %             [ zeros(length(A{7}),6) A{7}' zeros(length(A{7}),11-7)],...
            %             [ zeros(length(A{8}),7) A{8}' zeros(length(A{8}),11-8)],...
            %             [ zeros(length(A{9}),8) A{9}' zeros(length(A{9}),11-9)],...
            %             [ zeros(length(A{10}),9) A{10}' zeros(length(A{10}),11-10)],...
            %             [ zeros(length(A{11}),10) A{11}' ]);
            
            %     [ (0:5:240)' zeros(size(0:5:240,2),10)],...
            % choices = [ A{1}' zeros(length(A{1}),11-1)];
        end
        
        function inst_cost = inst_cost(obj,inst_vec)
            inst_cost = inst_vec * (prices_amz().get_hourly_price) ;
        end
        
        function [cost,cost1,penalty]=cost(obj, inst_vec, util, inst_interval)
            target_util = 60; % 0.8 utilizattion
            util_violation =  util - target_util .* ones(size(util,1),size(util,2));
            %               tmp(tmp>0)=0;
            %               penalty = sum(-1*tmp) ;
            util_violation(util_violation<0)=0;
            
            % hack: when model does not recognize there is no instance to
            % cover within the next control interval
            for tt=1:obj.control_interval
                if inst_interval(tt+60,:)==zeros(1,11)
                    util_violation(tt)=+inf;
                end
            end
            
            penalty = sum(util_violation) ;
            cost1 =  obj.inst_cost(inst_vec);
            % here we are adding (delta of dollars(instance hourly price
            % (e.g. 0.08 for small))) to (utilization deviation (e.g. 65))
            cost=100*cost1+1*penalty;
            %               disp(strcat('util:',num2str(util')));
            %               disp(strcat('tmp:',num2str(tmp')));
            %               disp(strcat('penalty:',num2str(penalty)));
            %               disp(strcat('cost1:',num2str(cost1)));
            %               disp(strcat('cost:',num2str(cost)));
            
        end
        
        function instance_quantity_to_fire=get_choice_at_time(obj,t)
            default=[0 0 0 0 0 0 0 0 0 0 0 ];
            if (mod(t-1, obj.control_interval) ~= 0)
                instance_quantity_to_fire = default;
                return;
            end
            
            choices = obj.generate_choices();
            choice_opt =[];
            cost_opt=+inf; cost_inst_opt=+inf; penalty_opt=+inf;
            for i=1:size(choices,1)
                choice=choices(i,:);
                res = obj.predict_choice_outcome(m,choice);
                [cost,cost_inst,penalty]=obj.cost(choice,res.util,res.inst);   % res.util is 60 values [system.t:system.t+60]
                if cost<cost_opt
                    choice_opt=choice;
                    cost_opt=cost;
                    cost_inst_opt=cost_inst;
                    penalty_opt=penalty;
                end
            end
            instance_quantity_to_fire = choice_opt;
            disp(strcat('choice_opt:' , num2str(choice_opt)));
            disp(strcat('cost_opt:' , num2str(cost_opt)));
            disp(strcat('cost_inst_opt:' , num2str(cost_inst_opt)));
            disp(strcat('penalty_opt:' , num2str(penalty_opt)));
        end
        
    
        %
        %        The difference between prediction and simulation is that in prediction, the past values of outputs used for calculation are measured values while in simulation the outputs are themselves a result of calculation using inputs and initial conditions.
        % The process of computing simulated and predicted responses over a time range starts by using the initial conditions to compute the first few output values.
        %         model = update_identified_model
        %         goal_path=indentify_trajectory(current_val)??
        %         current_action=argmin(|goal_path-model|+b|cost|)
        %         perform_action
        %
    end
    
    % %         function instance_quantity_to_fire=get_policy_output(obj,policy,t)
    % %         function res=plan(obj,system,duration,policy)
    % %         function plan=test_plan(obj)
    % %         function instance_quantity_to_fire=testPolicy(obj,t)
    % %         function plot_instances(obj,system)
    % %         function plan=probe_plan(obj)
    
    
end

